<template>
	<view class="poster-container">
		<!-- Canvas for rendering -->
		<canvas
			v-if="showCanvas"
			canvas-id="posterCanvas"
			id="posterCanvas"
			:style="{
				width: canvasWidth * dpr + 'rpx',
				height: canvasHeight * dpr + 'rpx',
				background: 'transparent',
			}"
			class="hidden-canvas"
		/>

		<!-- 触发按钮 -->
		<slot name="trigger" v-bind="{ generate, saving }">
			<button :loading="saving" @click="generate" class="btn-generate">
				生成海报
			</button>
		</slot>

		<!-- 海报预览 -->
		<image
			v-if="posterImage"
			:src="posterImage"
			mode="aspectFit"
			class="poster-image"
			@click="previewImage"
		/>

		<!-- 操作按钮 -->
		<view v-if="posterImage" class="actions">
			<button @click="savePoster" class="btn-save">保存到相册</button>
		</view>

		<!-- 加载中弹窗 -->
		<uni-popup v-model="loading" type="center" :mask-click="false">
			<view class="popup-content">
				<text>海报生成中...</text>
			</view>
		</uni-popup>
	</view>
</template>

<script>
export default {
	name: 'PosterGenerator',
	props: {
		config: { type: Object, required: true },
		showCanvas: { type: Boolean, default: false }
	},
	data() {
		return {
			posterImage: '',
			saving: false,
			loading: false,
			context: null,
			canvasWidth: 375,
			canvasHeight: 600,
			dpr: 1
		};
	},
	async mounted() {
		const info = uni.getSystemInfoSync();
		this.dpr = info.pixelRatio || 1;
	},
	methods: {
		async generate() {
			this.saving = true;
			this.loading = true;

			try {
				const config = this.mergeConfig();
				await this.$nextTick();
				await this.initCanvas(); 
				await this.drawPoster(config); 
			} catch (err) {
				console.error('海报生成失败:', err);
				uni.showToast({
					title: '失败: ' + (err.message || '未知错误'),
					icon: 'error',
					duration: 3000
				});
			} finally {
				this.saving = false;
				this.loading = false;
			}
		},

		mergeConfig() {
			return {
				width: 375,
				height: 600,
				background: '#fff',
				borderRadius: 12,
				elements: [],
				...this.config
			};
		},

		initCanvas() {
			return new Promise((resolve, reject) => {
				this.canvasWidth = this.config.width || 375;
				this.canvasHeight = this.config.height || 600;

				this.$nextTick(() => {
					setTimeout(() => {
						try {
							const context = uni.createCanvasContext('posterCanvas', this);
							context.scale(this.dpr, this.dpr);
							this.context = context;
							resolve();
						} catch (err) {
							reject(new Error('创建 Canvas Context 失败: ' + err.message));
						}
					}, 50);
				});
			});
		},

		async drawPoster(config) {
			const { width, height, background, borderRadius, elements } = config;
			const ctx = this.context;

			ctx.clearRect(0, 0, width, height);

			if (borderRadius > 0) {
				this.roundRect(ctx, 0, 0, width, height, borderRadius, background);
			} else {
				ctx.setFillStyle(background);
				ctx.fillRect(0, 0, width, height);
			}

			for (const el of elements) {
				await this.drawElement(ctx, el);
			}

			await this.finalDraw();

			await this.getFinalImage();
		},

		async drawElement(ctx, el) {
			const { type, x, y, width, height, radius, text, fontSize, color, fontWeight, textAlign, src } = el;

			if (type === 'text') {
				ctx.setFontSize(fontSize || 14);
				ctx.setFillStyle(color || '#000');
				ctx.setTextAlign(textAlign || 'left');
				ctx.fillText(text, x, y);
			}

			if (type === 'image' && src) {
				try {
					const tempPath = await this.loadImage(src); 
					if (radius > 0) {
						ctx.save();
						this.roundRect(ctx, x, y, width, height, radius, null); 
						ctx.clip();
						ctx.drawImage(tempPath, x, y, width, height);
						ctx.restore();
					} else {
						ctx.drawImage(tempPath, x, y, width, height);
					}
				} catch (err) {
					console.warn('图片加载失败:', src, err);
				}
			}
		},

		roundRect(ctx, x, y, w, h, r, fill) {
			const minSize = Math.min(w, h);
			const radius = Math.min(r, minSize / 2); 

			ctx.beginPath();
			ctx.moveTo(x + radius, y);
			ctx.arcTo(x + w, y, x + w, y + h, radius);
			ctx.arcTo(x + w, y + h, x, y + h, radius);
			ctx.arcTo(x, y + h, x, y, radius);
			ctx.arcTo(x, y, x + w, y, radius);
			ctx.closePath();

			if (fill) {
				ctx.setFillStyle(fill);
				ctx.fill();
			}
		},

		loadImage(src) {
			return new Promise((resolve, reject) => {
				const isNetwork = /^https?:\/\//.test(src);

				if (!isNetwork) {
					uni.getImageInfo({
						src,
						success: res => resolve(res.path),
						fail: err => reject(new Error(`本地图片失败: ${err.errMsg}`))
					});
				} else {
					uni.downloadFile({
						url: src,
						success: res => {
							if (res.statusCode !== 200) {
								reject(new Error(`下载失败: ${res.statusCode}`));
								return;
							}
							resolve(res.tempFilePath);
						},
						fail: err => {
							reject(new Error(`下载失败: ${err.errMsg}`));
						}
					});
				}
			});
		},

		finalDraw() {
			return new Promise((resolve, reject) => {
				this.context.draw(false, () => {
					resolve();
				});
			});
		},

		getFinalImage() {
			return new Promise((resolve, reject) => {
				uni.canvasToTempFilePath(
					{
						canvasId: 'posterCanvas',
						success: res => {
							this.posterImage = res.tempFilePath;
							uni.showToast({ title: '生成成功', icon: 'success' });
							resolve();
						},
						fail: err => {
							reject(new Error('导出失败: ' + JSON.stringify(err)));
						}
					},
					this
				);
			});
		},

		previewImage() {
			uni.previewImage({ urls: [this.posterImage] });
		},

		async savePoster() {
			try {
				await this.checkPermission();
				await uni.saveImageToPhotosAlbum({
					filePath: this.posterImage,
					success: () => uni.showToast({ title: '已保存' })
				});
			} catch {
				uni.showToast({ title: '保存失败', icon: 'error' });
			}
		},

		checkPermission() {
			// #ifdef MP-WEIXIN
			return new Promise((resolve, reject) => {
				uni.getSetting({
					success: res => {
						if (!res.authSetting['scope.writePhotosAlbum']) {
							uni.authorize({
								scope: 'scope.writePhotosAlbum',
								success: resolve,
								fail: () => {
									uni.showToast({ title: '请开启相册权限', icon: 'none' });
									reject();
								}
							});
						} else {
							resolve();
						}
					}
				});
			});
			// #endif
			return Promise.resolve();
		}
	}
};
</script>

<style scoped>
.poster-container {
	display: flex;
	flex-direction: column;
	align-items: center;
	padding: 20px;
}

/* 隐藏 canvas，但保留其渲染能力 */
.hidden-canvas {
	position: absolute;
	top: -9999px;
	left: -9999px;
	opacity: 0;
}

.btn-generate,
.btn-save {
	width: 80%;
	padding: 12px 0;
	margin: 10px 0;
	background-color: #007aff;
	color: #fff;
	border: none;
	border-radius: 24px;
	font-size: 16px;
}

.poster-image {
	width: 90%;
	height: 600px;
	margin: 15px 0;
	border-radius: 12px;
	border: 1px solid #eee;
}

.actions {
	width: 100%;
	display: flex;
	justify-content: center;
}

.popup-content {
	padding: 20px;
	background: #fff;
	border-radius: 12px;
	min-width: 200px;
	text-align: center;
}
</style>